package com.example.ephuizi.stl.util.point_light;

import android.opengl.GLES20;
import android.opengl.Matrix;
import android.util.Log;

import com.example.ephuizi.stl.R;
import com.example.ephuizi.stl.util.STLFile;
import com.example.ephuizi.stl.util.STLUtil;
import com.example.ephuizi.stl.util.ShaderHelper;

public class GLObjectForDraw {
    private static final String TAG = GLObjectForDraw.class.getSimpleName();

    /**
     * Size of the position data in elements.
     */
    static final int POSITION_DATA_SIZE = 3;

    /**
     * Size of the normal data in elements.
     */
    static final int NORMAL_DATA_SIZE = 3;

    /**
     * How many bytes per float.
     */
    static final int BYTES_PER_FLOAT = 4;


    private String vertexShader;//顶点着色器代码脚本
    private String fragmentShader;//片元着色器代码脚本

    /**
     * This is a handle to our cube shading program.
     */
    private int mProgramHandle;

    /**
     * These are handles to our texture data.
     */
    private int mMVPMatrixHandle;
    private int mMVMatrixHandle;
    private int mLightPosHandle;
    private int mPositionHandle;
    private int mANormalHandle;
    private int mColorHandle;
    private int mZoomHandle;

    /**
     * Used to hold a light centered on the origin in model space. We need a 4th coordinate so we can get translations to work when
     * we multiply this by our transformation matrices.
     */
    private final float[] mLightPosInModelSpace = new float[]{0.0f, 0.0f, 0.0f, 1.0f};//basic
    /**
     * Used to hold the current position of the light in world space (after transformation via model matrix).
     */
    private final float[] mLightPosInWorldSpace = new float[4];
    /**
     * Used to hold the transformed position of the light in eye space (after transformation via modelview matrix)
     */
    private final float[] mLightPosInEyeSpace = new float[4];

    /**
     * Stores a copy of the model matrix specifically for the light position.
     */
    private float[] mLightModelMatrix = new float[16];

    /**
     * Store the model matrix. This matrix is used to move models from object space (where each model can be thought
     * of being located at the center of the universe) to world space.
     */
    private float[] mModelMatrix = new float[16];//basic
    private float[] mTranslateMatrix = new float[16];

    /**
     * Store the view matrix. This can be thought of as our camera. This matrix transforms world space to eye space;
     * it positions things relative to our eye.
     */
    private float[] mViewMatrix = new float[16];//basic
    // mMVPMatrix is an abbreviation for "Model View Projection Matrix"
    private final float[] mMVMatrix = new float[16];
    private final float[] mMVPMatrix = new float[16];
    private final float[] mProjectionMatrix = new float[16];//basic
    /**
     * Store the accumulated rotation.
     */
    private final float[] mAccumulatedRotation = new float[16];

    /**
     * Store the current rotation.
     */
    private final float[] mCurrentRotation = new float[16];//basic

    /**
     * A temporary matrix.
     */
    private float[] mTemporaryMatrix = new float[16];

    private float mDeltaX = 0.0f;
    private float mDeltaY = 0.0f;

    private float mZoom = 0.01f;

    private float translateX = 0;
    private float translateY = 0;

    private float color[] = { 0.0902f, 0.3451f, 0.611764f, 1.0f };//model color
    private float muLightPos[] = {0.0f, 0.0f, 0.0f};


    private String stlPath;
    private STLFile stlFile;


    public GLObjectForDraw(MySurfaceView mv, String stlPath) {
        this.stlPath = stlPath;

        //初始化顶点坐标与着色数据
        initVertexData();
        Log.v(TAG, "initVertexData");
        //初始化shader
        intShader(mv);
        Log.v(TAG, "intShader");

        // set model scale to suitable screen
        initZoom();
        Log.e(TAG, "initZoom "+ getmZoom());
        // Position the eye in front of the origin.
        final float eyeX = 0.0f;
        final float eyeY = 0.0f;
        final float eyeZ = -0.5f;

        // We are looking toward the distance
        final float lookX = 0.0f;
        final float lookY = 0.0f;
        final float lookZ = -1.0f;

        // Set our up vector. This is where our head would be pointing were we holding the camera.
        final float upX = 0.0f;
        final float upY = 1.0f;
        final float upZ = 0.0f;

        // Set the view matrix. This matrix can be said to represent the camera position.
        // NOTE: In OpenGL 1, a ModelView matrix is used, which is a combination of a model and
        // view matrix. In OpenGL 2, we can keep track of these matrices separately if we choose.
        Matrix.setLookAtM(mViewMatrix, 0, eyeX, eyeY, eyeZ, lookX, lookY, lookZ, upX, upY, upZ);

        // Initialize the accumulated rotation matrix
        Matrix.setIdentityM(mAccumulatedRotation, 0);
    }

    //初始化顶点坐标与着色数据的方法
    public void initVertexData() {
        stlFile = STLUtil.parse(stlPath);
    }

    //初始化shader
    public void intShader(MySurfaceView mv) {
        //加载顶点着色器的脚本内容
        vertexShader = ShaderHelper.loadSource(mv.getContext(), R.raw.vertex_shader);
        //加载片元着色器的脚本内容
        fragmentShader = ShaderHelper.loadSource(mv.getContext(), R.raw.fragment_shader);

        final int vertexShaderHandle = ShaderHelper.compileShader(GLES20.GL_VERTEX_SHADER, vertexShader);
        final int fragmentShaderHandle = ShaderHelper.compileShader(GLES20.GL_FRAGMENT_SHADER, fragmentShader);

        //基于顶点着色器与片元着色器创建程序
        mProgramHandle = ShaderHelper.createAndLinkProgram(vertexShaderHandle, fragmentShaderHandle,
                new String[]{"a_Position", "a_Normal", "a_TexCoordinate"});

    }

    private void initZoom() {
        float m = stlFile.absMax();
        this.setmZoom(1.0f / m);
    }

    public void draw() {
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);

        // Set our per-vertex lighting program.
        GLES20.glUseProgram(mProgramHandle);

        // Set program handles for cube drawing.
        mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgramHandle, "u_MVPMatrix");
        mMVMatrixHandle = GLES20.glGetUniformLocation(mProgramHandle, "u_MVMatrix");
        mLightPosHandle = GLES20.glGetUniformLocation(mProgramHandle, "u_LightPos");
        mZoomHandle = GLES20.glGetUniformLocation(mProgramHandle, "u_Zoom");
        mColorHandle = GLES20.glGetUniformLocation(mProgramHandle, "vColor");
        mPositionHandle = GLES20.glGetAttribLocation(mProgramHandle, "a_Position");
        mANormalHandle = GLES20.glGetAttribLocation(mProgramHandle, "a_Normal");

        // Calculate position of the light. Push into the distance.
        Matrix.setIdentityM(mLightModelMatrix, 0);
        Matrix.translateM(mLightModelMatrix, 0, 0.0f, 0.0f, -1.0f);

        Matrix.multiplyMV(mLightPosInWorldSpace, 0, mLightModelMatrix, 0, mLightPosInModelSpace, 0);
        Matrix.multiplyMV(mLightPosInEyeSpace, 0, mViewMatrix, 0, mLightPosInWorldSpace, 0);
        // Pass in the light position in eye space.
        GLES20.glUniform3f(mLightPosHandle, mLightPosInEyeSpace[0], mLightPosInEyeSpace[1], mLightPosInEyeSpace[2]);

        // Translate the cube into the screen.
        Matrix.setIdentityM(mTranslateMatrix, 0);
        Matrix.translateM(mTranslateMatrix, 0, getTranslateX() * getmZoom(), getTranslateY() * getmZoom(), -2.0f);

        // Set a matrix that contains the current rotation.
        Matrix.setIdentityM(mCurrentRotation, 0);
        Matrix.rotateM(mCurrentRotation, 0, mDeltaX, 0.0f, 1.0f, 0.0f);
        Matrix.rotateM(mCurrentRotation, 0, mDeltaY, 1.0f, 0.0f, 0.0f);
        mDeltaX = 0.0f;
        mDeltaY = 0.0f;

        // Multiply the current rotation by the accumulated rotation, and then set the accumulated rotation to the result.
        Matrix.multiplyMM(mTemporaryMatrix, 0, mCurrentRotation, 0, mAccumulatedRotation, 0);
        System.arraycopy(mTemporaryMatrix, 0, mAccumulatedRotation, 0, 16);

        Matrix.setIdentityM(mModelMatrix, 0);
        Matrix.multiplyMM(mModelMatrix, 0, mTranslateMatrix,0,mAccumulatedRotation,0);
        Matrix.scaleM(mModelMatrix, 0, getmZoom(), getmZoom(), getmZoom());

        // This multiplies the view matrix by the model matrix, and stores
        // the result in the MVM matrix
        // (which currently contains model * view).
        Matrix.multiplyMM(mMVMatrix, 0, mViewMatrix, 0, mModelMatrix, 0);

        // Pass in the modelview matrix.
        GLES20.glUniformMatrix4fv(mMVMatrixHandle, 1, false, mMVMatrix, 0);//u_MVMatrix
        ShaderHelper.checkGlError("mMVMatrixHandle");

        // This multiplies the modelview matrix by the projection matrix,
        // and stores the result in the MVP matrix
        // (which now contains model * view * projection).
        Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mMVMatrix, 0);//注意是左乘还是右乘


        GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mMVPMatrix, 0);
        ShaderHelper.checkGlError("mMVPMatrixHandle");

        GLES20.glUniform3fv(mLightPosHandle, 1, muLightPos, 0);
        ShaderHelper.checkGlError("mLightPosHandle");

        GLES20.glUniform1f(mZoomHandle, getmZoom());

        //启用顶点位置、法向量、纹理坐标数据
        // Set color for drawing the triangle
        GLES20.glUniform4fv(mColorHandle, 1, color, 0);

        GLES20.glEnableVertexAttribArray(mPositionHandle);
        //将顶点位置数据
        GLES20.glVertexAttribPointer
                (
                        mPositionHandle,
                        POSITION_DATA_SIZE,
                        GLES20.GL_FLOAT,
                        false,
                        POSITION_DATA_SIZE * BYTES_PER_FLOAT,
                        stlFile.getVertexBuffer()
                );
        ShaderHelper.checkGlError("mPositionHandle");

        GLES20.glEnableVertexAttribArray(mANormalHandle);
        GLES20.glVertexAttribPointer
                (
                        mANormalHandle,
                        NORMAL_DATA_SIZE,
                        GLES20.GL_FLOAT,
                        false,
                        NORMAL_DATA_SIZE * BYTES_PER_FLOAT,
                        stlFile.getNormalBuffer()
                );
        ShaderHelper.checkGlError("mANormalHandle");

        //绘制加载的物体
        GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, stlFile.getFacetNum() * 3);
        GLES20.glDisableVertexAttribArray(mPositionHandle);
        GLES20.glDisableVertexAttribArray(mANormalHandle);
    }

    public float[] getmMVPMatrix() {
        return mMVPMatrix;
    }

    public float[] getmProjectionMatrix() {
        return mProjectionMatrix;
    }

    public float[] getmViewMatrix() {
        return mViewMatrix;
    }

    public float getmZoom() {
        return mZoom;
    }

    public void setmZoom(float mZoom) {
        this.mZoom = mZoom;
    }

    public float getTranslateX() {
        return translateX;
    }

    public void setTranslateX(float translateX) {
        this.translateX = translateX;
    }

    public float getTranslateY() {
        return translateY;
    }

    public void setTranslateY(float translateY) {
        this.translateY = translateY;
    }

    /**
     * 设置模型颜色
     *
     * @param r
     * @param b
     * @param g
     * @return
     */
    public boolean setModelColor(int r, int b, int g) {
        if ((r > 0 && r < 255) && (g > 0 && g < 255) && (b > 0 && b < 255)) {
            this.color[0] = r;
            this.color[1] = g;
            this.color[2] = b;
            return true;
        }
        return false;
    }

    public float getmDeltaX() {
        return mDeltaX;
    }

    public void setmDeltaX(float mDeltaX) {
        this.mDeltaX = mDeltaX;
    }

    public float getmDeltaY() {
        return mDeltaY;
    }

    public void setmDeltaY(float mDeltaY) {
        this.mDeltaY = mDeltaY;
    }
}
